<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="Python类型注释, python,machine learning,deep learning,html,css,c,c++,cpp,cmake,ros,linux,ubuntu">
    <meta name="description" content="本文系统的介绍了Python中的类型注释">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="referrer" content="no-referrer-when-downgrade">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>Python类型注释 | JackWang&#39;s Blog</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery-3.6.0.min.js"></script>

<meta name="generator" content="Hexo 5.4.2">
<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css">
<link rel="stylesheet" href="/css/prism-line-numbers.css" type="text/css"></head>



   <style>
    body{
       background-image: url(https://cdn.jsdelivr.net/gh/Tokisaki-Galaxy/res/site/medias/background.jpg);
       background-repeat:no-repeat;
       background-size: 100% 100%;
       background-attachment:fixed;
    }
</style>



<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">JackWang&#39;s Blog</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="" class="waves-effect waves-light">

      
      <i class="fas fa-book-reader" style="zoom: 0.6;"></i>
      
      <span>博客</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/tags">
          
          <i class="fas fa-tags" style="margin-top: -20px; zoom: 0.6;"></i>
          
	  <span>按标签归类文章</span>
        </a>
      </li>
      
      <li>
        <a href="/categories">
          
          <i class="fas fa-bookmark" style="margin-top: -20px; zoom: 0.6;"></i>
          
	  <span>按目录归类文章</span>
        </a>
      </li>
      
      <li>
        <a href="/archives">
          
          <i class="fas fa-archive" style="margin-top: -20px; zoom: 0.6;"></i>
          
	  <span>按日期分类文章</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>



<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">JackWang&#39;s Blog</div>
        <div class="logo-desc">
            
            JackWang的个人博客
            
        </div>
    </div>

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-book-reader"></i>
			
			博客
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  style="background:  ;" >
              
                <li>

                  <a href="/tags " style="margin-left:75px">
				  
				   <i class="fa fas fa-tags" style="position: absolute;left:50px" ></i>
			      
                              <span>按标签归类文章</    span>

                  </a>
                </li>
              
                <li>

                  <a href="/categories " style="margin-left:75px">
				  
				   <i class="fa fas fa-bookmark" style="position: absolute;left:50px" ></i>
			      
                              <span>按目录归类文章</    span>

                  </a>
                </li>
              
                <li>

                  <a href="/archives " style="margin-left:75px">
				  
				   <i class="fa fas fa-archive" style="position: absolute;left:50px" ></i>
			      
                              <span>按日期分类文章</    span>

                  </a>
                </li>
              
            </ul>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    
<script src="/libs/cryptojs/crypto-js.min.js"></script>
<script>
    (function() {
        let pwd = '';
        if (pwd && pwd.length > 0) {
            if (pwd !== CryptoJS.SHA256(prompt('抱歉，这篇文章并不想让所有人都看到，请输入授权密码观看')).toString(CryptoJS.enc.Hex)) {
                alert('密码错误，将返回主页！');
                location.href = '/';
            }
        }
    })();
</script>




<div class="bg-cover pd-header post-cover" style="background-image: url('https://jack-1307599355.cos.ap-shanghai.myqcloud.com/carbon.png')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">Python类型注释</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/Python/">
                                <span class="chip bg-color">Python</span>
                            </a>
                        
                            <a href="/tags/%E6%9D%82%E9%A1%B9/">
                                <span class="chip bg-color">杂项</span>
                            </a>
                        
                            <a href="/tags/Python-Type-Hint/">
                                <span class="chip bg-color">Python Type Hint</span>
                            </a>
                        
                            <a href="/tags/Python-%E7%B1%BB%E5%9E%8B%E6%B3%A8%E9%87%8A/">
                                <span class="chip bg-color">Python 类型注释</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2023-11-24
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>更新日期:&nbsp;&nbsp;
                    2023-11-25
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    7.6k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    29 分
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        

        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/carbon.png" alt="Python类型注释"></p>
<h1 id="Python类型注释"><a href="#Python类型注释" class="headerlink" title="Python类型注释"></a>Python类型注释</h1><blockquote>
<p>本文编写于2023年11月24日，Python的Type Hint因为正在经历快速的开发迭代，因此本文针对最新的Type Hint进行讲解。</p>
</blockquote>
<h2 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h2><p>Python是一种动态类型语言，这意味着我们不需要在声明变量时指定其类型，Python解释器会在运行时自动确定变量的类型。</p>
<h3 id="1-1-绑定（Binding）"><a href="#1-1-绑定（Binding）" class="headerlink" title="1.1 绑定（Binding）"></a>1.1 绑定（Binding）</h3><blockquote>
<p>在Python中，<code>Binding</code>是一个重要的概念。</p>
</blockquote>
<p>在Python中，变量名和对象之间的关联被称为<code>Binding</code>。Python中的所有东西都是对象。这包括数字（如整数和浮点数）、字符串、列表、字典、函数、类，甚至模块等等。这些都是对象，它们都有一个类型，它们都可以有属性和方法。</p>
<p>而当我们在Python中声明一个变量的时候，Python干的事情就是：</p>
<ul>
<li>首先在内存中创建一个对应类型的对象</li>
<li>然后将内存中对象绑定（<code>bind</code>）到这个变量名上</li>
</ul>
<p>未来我们就可以通过变量名来访问这个对象的属性和方法。例如：</p>
<pre class="line-numbers language-python"><code class="language-python">x <span class="token operator">=</span> <span class="token number">56</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>Python在内存中为我们创建了一个 <code>int</code> 对象，值为56，然后将其与变量名 <code>x</code> 绑定。接下来我们就可以使用<code>int</code>这个类的方法了，例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">print</span><span class="token punctuation">(</span>x<span class="token punctuation">.</span>to_bytes<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true"># Output: b'8'</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>而<code>8</code>就是<code>ASCII</code>码中<code>56</code>的值</p>
<blockquote>
<p>PS: 我们这里不讨论Python会提前创建一些对象以加速运行</p>
</blockquote>
<p><code>Binding</code>为Python带来了极大的灵活性，允许我们编写更通用的代码。</p>
<h3 id="1-2-鸭子类型（Duck-Type）"><a href="#1-2-鸭子类型（Duck-Type）" class="headerlink" title="1.2 鸭子类型（Duck Type）"></a>1.2 鸭子类型（Duck Type）</h3><p>“鸭子类型”（Duck Typing）是一种编程概念，它在动态类型的语言中，如Python，非常常见。这个概念的名字来源于一个古老的表达：</p>
<ul>
<li><strong>如果它走起路来像鸭子，叫起来也像鸭子，那么它就是鸭子</strong></li>
</ul>
<p>在编程语境中，鸭子类型意味着我们并不关心对象是什么类型，我们只关心对象能做什么。换句话说，一个对象的行为（即它的方法和属性）比它的实际类或类型更重要。</p>
<p>例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Duck</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">quack</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token string">"Quack!"</span>

<span class="token keyword">class</span> <span class="token class-name">Dog</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">quack</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token string">"I pretend to be a duck. Quack!"</span>

<span class="token keyword">def</span> <span class="token function">make_it_quack</span><span class="token punctuation">(</span>animal<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span>animal<span class="token punctuation">.</span>quack<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

duck <span class="token operator">=</span> Duck<span class="token punctuation">(</span><span class="token punctuation">)</span>
dog <span class="token operator">=</span> Dog<span class="token punctuation">(</span><span class="token punctuation">)</span>

make_it_quack<span class="token punctuation">(</span>duck<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># Output: Quack!</span>
make_it_quack<span class="token punctuation">(</span>dog<span class="token punctuation">)</span>   <span class="token comment" spellcheck="true"># Output: I pretend to be a duck. Quack!</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>在鸭子类型的观念下，我们并不关心传递给 <code>make_it_quack</code> 的对象是什么类型，只要传入的对象有一个 <code>.quack()</code> 方法就可以。这意味着我们可以传递任何实现了 <code>.quack()</code> 方法的对象给这个函数，而不仅仅是某个特定的 <code>Duck</code> 类的实例。从而使得<code>make_it_quack</code>函数更加通用，避免了<code>C++</code>中需要对同一个函数进行重载或者使用模板。</p>
<h3 id="1-3-问题"><a href="#1-3-问题" class="headerlink" title="1.3 问题"></a>1.3 问题</h3><p>然而，随着Python在大规模和复杂的项目中的广泛应用，<code>Binding</code>带来的一些缺点开始显现出来：</p>
<ol>
<li><strong>可读性和可维护性</strong>：在没有类型信息的情况下，阅读和理解代码可能会变得困难，特别是对于大型的代码库。重重跳转之后就不知道某个变量到底是什么类型了</li>
<li><strong>错误检测</strong>：动态类型语言在运行时才检查类型错误，有可能某个地方将变量名绑定到了另外一个类的实例上，如果恰好也有相同的方法（即鸭子类型），这可能导致一些难以发现的bug</li>
</ol>
<h3 id="1-4-解决之道：-类型注解（Type-Hint）"><a href="#1-4-解决之道：-类型注解（Type-Hint）" class="headerlink" title="1.4 解决之道： 类型注解（Type Hint）"></a>1.4 解决之道： 类型注解（Type Hint）</h3><p>Python动态类型带来的最主要的问题就是我们不知道某个变量绑定的对象的类型，因此如果只要我们能够知道某个类的类型，就可以解决这些问题。</p>
<p>因此Python中引入了<code>类型注释</code>（<code>Type Hint</code>）的功能，使得开发者可以选择性地为变量、函数参数和返回值添加类型信息:</p>
<ul>
<li>一方面，这些类型信息告知了其他审阅你代码的人这个变量应该是什么类型，方便了维护和理解</li>
<li>另外一方面，一些工具可以在不运行代码的前提下检查这些类型，从而减少实际调试中的难度</li>
<li>第三，类型注解也可以被IDE和其他工具用来提供更好的代码补全和错误检查功能，在推断出变量的类型后，IDE可以更快、更轻松的给出补全信息</li>
</ul>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/mypy_light.svg" alt="mypy是一个Python类型静态检查工具" style="zoom:33%;"></p>
<p>所以Type Hint对Python来说还是非常有用的，在精进Python的路上还是有必要要学习一下的。</p>
<h2 id="2-Type-Hint发展历史"><a href="#2-Type-Hint发展历史" class="headerlink" title="2. Type Hint发展历史"></a>2. Type Hint发展历史</h2><p>从<code>PEP</code>上的讨论，我们能够得知<code>Type Hint</code>的发展历史。要了解Python <code>Type Hint</code>的发展历史，就得先了解什么是<code>PEP</code>。</p>
<blockquote>
<p>这部分的内容如果不感兴趣的话，可以直接跳转到后面介绍Type Hint语法的部分</p>
</blockquote>
<h3 id="2-1-PEP-（Python-Enhancement-Proposal）"><a href="#2-1-PEP-（Python-Enhancement-Proposal）" class="headerlink" title="2.1 PEP （Python Enhancement Proposal）"></a>2.1 PEP （Python Enhancement Proposal）</h3><p><code>PEP</code>是<code>Python Enhancement Proposal</code>的缩写，意为Python增强提案。它是Python社区提出和讨论Python语言改进和新特性的一种正式机制。</p>
<p>每个PEP都是一个设计文档，可能描述了一个新的特性，或者解释了某个问题，或者描述了一些新的或修订的过程或环境。PEP应该提供足够的信息来帮助社区理解提案的问题和解决方案。</p>
<p><code>PEP</code>分为三种类型：</p>
<ol>
<li><strong>Standards Track PEPs</strong>：这些PEP描述了新的特性或实现，或者描述了一个问题并提出了解决方案。在未来的Python版本中可能会引入这些新的特性。</li>
<li><strong>Informational PEPs</strong>：这些PEP描述了Python设计问题，或者为Python社区提供了一些通用的信息或指导。但是，这些PEP通常只是讨论，只有足够的有用之后才可能被正式纳入到Python未来的改进中</li>
<li><strong>Process PEPs</strong>：这些PEP描述了Python社区的过程，或者提出了一个过程改进。它们类似于Standards Track PEPs，但是它们通常不改变Python的实现，只改变Python社区的流程，即对Python社区改进提出了意见</li>
</ol>
<p>一个PEP的声明周期通常包括草案提出、讨论、修改、投票，以及最终的接受或拒绝。所有的PEP都可以在Python的官方网站上找到：<a target="_blank" rel="noopener" href="https://peps.python.org/">https://peps.python.org/</a></p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/image-20231124212448624.png" alt="Python PEP社区" style="zoom:33%;"></p>
<h3 id="2-2-PEP中Type-Hint的发展历史"><a href="#2-2-PEP中Type-Hint的发展历史" class="headerlink" title="2.2 PEP中Type Hint的发展历史"></a>2.2 PEP中Type Hint的发展历史</h3><p>简要的历史如下：</p>
<ul>
<li><strong>PEP 3107 - Function Annotations</strong>：在Python 3.0中引入，这个PEP提供了函数注解的基础语法，但并没有规定该如何解析注释，只是提供了一个建议。</li>
<li><strong>PEP 484 - Type Hints</strong>：在Python 3.5中引入，这个PEP正式引入了类型提示系统。它定义了基本的类型提示语法，以及一些标准的类型（如<code>List</code>、<code>Dict</code>等），并引入了<code>typing</code>模块。</li>
<li><strong>PEP 526 - Syntax for Variable Annotations</strong>：在Python 3.6中引入，这个PEP扩展了类型提示系统，允许在变量声明中使用注解来指定变量的预期类型。</li>
<li><strong>PEP 563 - Postponed Evaluation of Type Annotations</strong>：在Python 3.7中引入，这个PEP改变了类型注解的解析方式，使得它们在运行时不会立即被评估。这解决了一些关于循环引用和前向引用的问题。</li>
<li><strong>PEP 585 - Type Hinting Generics In Standard Collections</strong>：在Python 3.9中引入，这个PEP允许直接在标准集合类型（如<code>list</code>、<code>dict</code>等）上使用泛型类型提示，而不需要使用<code>typing</code>模块中的对应类型（如<code>List</code>、<code>Dict</code>等）。</li>
<li><strong>PEP 646 - Variadic Generics</strong>：在Python 3.10中引入，这个PEP引入了可变泛型，允许在类型提示中更准确地表示可变数量的类型。</li>
<li><strong>PEP 612 - Parameter Specification in Type Hints</strong>：在Python 3.10中引入，这个PEP提供了一种新的方式来表示复杂的参数类型，特别是那些使用了<code>*args</code>和<code>**kwargs</code>的函数。</li>
<li><strong>PEP 604 - Allow writing union types as X | Y</strong>：在Python 3.10中引入，这个PEP允许使用<code>X | Y</code>的语法来表示联合类型，而不需要使用<code>typing.Union</code>。</li>
</ul>
<h4 id="PEP-3107-Function-Annotations"><a href="#PEP-3107-Function-Annotations" class="headerlink" title="PEP 3107: Function Annotations"></a>PEP 3107: Function Annotations</h4><p><code>PEP 3107</code>提出于2006年12月2日，最终在Python 3.10中引入。最初的目的是希望给<code>Python</code>的函数中添加注释，从而帮助读者理解函数的每个类型，从而避免鸭子类型带来的烦恼。</p>
<p>在PEP 3107之前，Python的函数声明看起来大概是这样的：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">foo</span><span class="token punctuation">(</span>bar<span class="token punctuation">,</span> baz<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>我们完全不知道<code>bar</code>和<code>baz</code>到底是什么类型的参数，从而导致难以理解<code>foo</code>函数的作用。</p>
<p>而<code>PEP 3107</code>提出了一个建议，就是为<code>Python</code>引入新的语法，允许你在函数声明中添加注解，例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">foo</span><span class="token punctuation">(</span>bar<span class="token punctuation">:</span> <span class="token string">'a string'</span><span class="token punctuation">,</span> baz<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> float<span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>在这个例子中，<code>'a string'</code>和<code>int</code>是参数注解，<code>float</code>是返回注解。<code>PEP 3107</code>中提出，注解可以是任何Python表达式，它们在函数定义时被评估，并在函数的<code>__annotations__</code>属性中存储，以帮助静态类型检查工具进行检查。</p>
<p>需要注意的是，<code>PEP 3107</code>本身并没有定义该如何解析注解，只是提出了一个建议。注解的解析由第三方库和工具决定。</p>
<h4 id="PEP-484-Type-Hints"><a href="#PEP-484-Type-Hints" class="headerlink" title="PEP 484: Type Hints"></a>PEP 484: Type Hints</h4><p>尽管<code>PEP 3107</code>中只是提出了该如何对函数原型进行注释，但是其中已经出现了对变量的类型注释。因此经过了几个版本的发展后，终于Type Hint在PEP 484中被进一步讨论，并被引入了Python 3.5版本中。</p>
<blockquote>
<p>值得一提的是，<code>PEP 484</code>是<code>Python</code>之父Guido van Rossum亲自提出得，可见<code>Type Hint</code>之重要性</p>
</blockquote>
<p><code>PEP 484</code>正式的为Python引入了类型注释（<code>Type Hints</code>）系统，这是一种标准化的方式来表明函数参数和返回值的预期类型，允许开发者在代码中明确地指定变量、函数参数和返回值的预期类型。</p>
<p>例如，下面的函数使用类型提示来表明参数 <code>x</code> 和 <code>y</code> 应该是整数，返回值应该是浮点数：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">divide</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> int<span class="token punctuation">,</span> y<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> float<span class="token punctuation">:</span>
    <span class="token keyword">return</span> x <span class="token operator">/</span> y
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>除了正式规定<code>Type Hint</code>的语法之外，<code>PEP 484</code>还定义了一个 <code>typing</code> 模块，提供了一些用于表示复杂类型的类和函数，例如 <code>List</code>、<code>Dict</code>、<code>Tuple</code>、<code>Optional</code> 等。例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> List<span class="token punctuation">,</span> Tuple

<span class="token keyword">def</span> <span class="token function">process_data</span><span class="token punctuation">(</span>data<span class="token punctuation">:</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
    <span class="token keyword">for</span> x<span class="token punctuation">,</span> y <span class="token keyword">in</span> data<span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h4 id="PEP-526-Syntax-for-Variable-Annotations"><a href="#PEP-526-Syntax-for-Variable-Annotations" class="headerlink" title="PEP 526: Syntax for Variable Annotations"></a>PEP 526: Syntax for Variable Annotations</h4><p>在<code>PEP 3107</code>和<code>PEP 484</code>中，类型注解都仅针对函数，而没有为变量提供类型注解。因此<code>PEP 526</code>扩展了<code>Type Hint</code>，允许在变量声明中使用注解来指定变量的预期类型。</p>
<p>例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># 注释Python内置类型的变量</span>
x<span class="token punctuation">:</span> int <span class="token operator">=</span> <span class="token number">1</span>

<span class="token comment" spellcheck="true"># 注释自定义类型的变量</span>
<span class="token keyword">class</span> <span class="token class-name">MyClass</span><span class="token punctuation">:</span>
    <span class="token keyword">pass</span>

y<span class="token punctuation">:</span> MyClass <span class="token operator">=</span> MyClass<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment" spellcheck="true"># 注释变量的类型</span>
z<span class="token punctuation">:</span> int

<span class="token comment" spellcheck="true"># 为类属性注释类型</span>
<span class="token keyword">class</span> <span class="token class-name">MyClass</span><span class="token punctuation">:</span>
    x<span class="token punctuation">:</span> int
    y<span class="token punctuation">:</span> str <span class="token operator">=</span> <span class="token string">"hello"</span>

<span class="token comment" spellcheck="true"># 注释一个所有元素为int的list</span>
numbers<span class="token punctuation">:</span> List<span class="token punctuation">[</span>int<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h4 id="PEP-563-Postponed-Evaluation-of-Type-Annotations"><a href="#PEP-563-Postponed-Evaluation-of-Type-Annotations" class="headerlink" title="PEP 563: Postponed Evaluation of Type Annotations"></a>PEP 563: Postponed Evaluation of Type Annotations</h4><p>在<code>PEP 563</code>之前，类型注解在定义时就会被解析。这意味着如果你在类型注解中引用了一个还没有定义的类，Python会抛出一个<code>NameError</code>。</p>
<p>例如下面定义链表节点的代码：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Node</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">add_child</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> node<span class="token punctuation">:</span> <span class="token string">"Node"</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>  <span class="token comment" spellcheck="true"># Python在解析Type Hint时，解析到这里，因为Node还没有被完全定义，所以如果不加引号会报一个NameError的错误</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>因此，<code>PEP 563</code>中提出：为了解决这个问题，可以将类型注解的解析推迟到了运行时。这意味着你可以在类型注解中直接引用还没有定义的类，而不需要使用字符串。以上面的例子为例，使用PEP 563，我们可以这样写：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> __future__ <span class="token keyword">import</span> annotations

<span class="token keyword">class</span> <span class="token class-name">Node</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">add_child</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> node<span class="token punctuation">:</span> Node<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>  <span class="token comment" spellcheck="true"># 在这里，Node可以直接被引用</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>需要注意的是，虽然<code>PEP 563</code>在Python 3.7中被引入，但是你需要通过<code>from __future__ import annotations</code>来启用它。在Python 3.10及以后的版本中，PEP 563成为了默认行为，不再需要导入<code>__future__</code>模块。</p>
<blockquote>
<p>在Python中，<code>__future__</code>模块是一个特殊的模块，它允许你在当前的Python版本中使用一些在未来Python版本才会被引入的新特性。新特性通常是在PEP中讨论之后决定要引入的新特性。</p>
<p>这个模块的名字中的”future”并不是指真正的未来，而是指未来的Python版本。当Python的核心开发者决定引入一些可能会改变Python语法或者语义的新特性时，他们通常会首先将这些新特性作为<code>__future__</code>模块中的可选特性引入，以便开发者在未来的Python版本正式引入这些新特性之前就可以开始使用它们。</p>
<p>这样做的好处是，开发者可以在新特性正式引入并成为默认行为之前，就有足够的时间来适应这些新特性。同时，这也给了开发者一个机会，让他们可以在新特性正式引入之前，就对它们进行测试，以确保它们不会引入新的bug。</p>
</blockquote>
<h4 id="PEP-585-Type-Hinting-Generics-In-Standard-Collections"><a href="#PEP-585-Type-Hinting-Generics-In-Standard-Collections" class="headerlink" title="PEP 585: Type Hinting Generics In Standard Collections"></a>PEP 585: Type Hinting Generics In Standard Collections</h4><p><code>PEP 585</code>之前，对<code>list</code>、<code>dict</code>等复合类型进行注释需要从<code>Typing</code>模块中导入<code>List</code>、<code>Dict</code>等对象。而<code>PEP 585</code>对此进行了改进，使得标准集合类型如<code>list</code>，<code>dict</code>，<code>set</code>和<code>tuple</code>可以直接用于类型提示，而不再需要从<code>typing</code>模块导入对应的大写形式，如<code>List</code>，<code>Dict</code>，<code>Set</code>和<code>Tuple</code>。</p>
<p>在PEP 585之前，如果你想给一个列表变量添加类型注解，你需要这样写：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> List

<span class="token keyword">def</span> <span class="token function">greet_all</span><span class="token punctuation">(</span>names<span class="token punctuation">:</span> List<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
    <span class="token keyword">for</span> name <span class="token keyword">in</span> names<span class="token punctuation">:</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"Hello, {name}!"</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>但是在PEP 585之后，你可以直接使用内置的<code>list</code>类型作为类型注解：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">greet_all</span><span class="token punctuation">(</span>names<span class="token punctuation">:</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
    <span class="token keyword">for</span> name <span class="token keyword">in</span> names<span class="token punctuation">:</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"Hello, {name}!"</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>这样的改变使得类型注解的语法更加简洁和直观。此外，它还允许我们在类型注解中使用更多的内置类型，例如<code>type</code>和<code>collections.abc</code>模块中的类型。</p>
<p>需要注意的是，虽然PEP 585在Python 3.9中被引入，但是直到Python 3.10，才将<code>from __future__ import annotations</code>的行为作为默认行为。这意味着在Python 3.9中，如果你想使用PEP 585的特性，你需要在你的代码中添加<code>from __future__ import annotations</code>这一行。</p>
<h4 id="PEP-646-Variadic-Generics"><a href="#PEP-646-Variadic-Generics" class="headerlink" title="PEP 646: Variadic Generics"></a>PEP 646: Variadic Generics</h4><p><code>PEP 646</code>中提出可以在Python类型注释系统中引入变长泛型的提议。变长泛型是一种允许泛型接受任意数量类型参数的特性。</p>
<p>在一些情况下，我们可能需要表示一个元组的长度是未知的，或者一个函数可以接受任意数量的参数，这些参数的类型都是已知的。在这些情况下，变长泛型就非常有用。</p>
<p>例如，假设我们有一个函数，这个函数接受一个字符串和一个元组，元组中的每个元素都是整数。我们可以使用<code>Tuple[int, ...]</code>来表示这个元组的类型。但是，如果我们想要表示这个元组中的每个元素都可以是不同的类型，那么我们就需要使用变长泛型。</p>
<p>在PEP 646中，我们可以使用<code>Tuple[*Ts]</code>来表示一个元组，其中<code>Ts</code>使用<code>PEP 646</code>中提出的语法进行定义，用以表示多种类型。例如，定义<code>Ts</code>为<code>（int, str, float）</code>，则<code>Tuple[*Ts]</code>可以表示一个元素可以是<code>int</code>，<code>str</code>，<code>float</code>等任何类型的元组</p>
<h4 id="PEP-612-Parameter-Specification-in-Type-Hints"><a href="#PEP-612-Parameter-Specification-in-Type-Hints" class="headerlink" title="PEP 612: Parameter Specification in Type Hints"></a>PEP 612: Parameter Specification in Type Hints</h4><p>在Python中，任何东西都是对象，函数也不例外。函数是一个<code>function object</code>，对应于<code>function class</code>，具有<code>__call__</code>方法。例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">add</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token keyword">return</span> x <span class="token operator">+</span> y

add<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>当定义函数的时候，即<code>def add(x, y)</code>时，Python会创建一个<code>function object</code>，然后把这个<code>function object</code>绑定到<code>add</code>这个变量上。我们调用<code>add(1,2)</code>时候实际上就是调用<code>add.__call(1,2)__</code>这个方法。</p>
<p>正是因为Python的函数也是一个对象这个特性，在Python中函数也可以作为参数，传入到别的函数中，或者其他函数的返回值。<strong>这种以函数为参数或返回值的函数称为高阶函数</strong>。例如：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># 定义一个函数，接收两个参数：一个函数f和一个值x</span>
<span class="token keyword">def</span> <span class="token function">apply_function</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> x<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> f<span class="token punctuation">(</span>x<span class="token punctuation">)</span>

<span class="token comment" spellcheck="true"># 定义一个简单的函数，计算一个数的平方</span>
<span class="token keyword">def</span> <span class="token function">square</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> x<span class="token operator">**</span><span class="token number">2</span>

<span class="token comment" spellcheck="true"># 我们可以将square这个函数对象作为参数传递给apply_function函数</span>
result <span class="token operator">=</span> apply_function<span class="token punctuation">(</span>square<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 输出25，因为5的平方是25</span>

<span class="token comment" spellcheck="true"># 函数也可以作为其他函数的返回值</span>
<span class="token keyword">def</span> <span class="token function">get_operation</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> name <span class="token operator">==</span> <span class="token string">'square'</span><span class="token punctuation">:</span>
        <span class="token keyword">def</span> <span class="token function">square</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> x<span class="token operator">**</span><span class="token number">2</span>
        <span class="token keyword">return</span> square  <span class="token comment" spellcheck="true"># 返回一个函数对象</span>
    <span class="token keyword">elif</span> name <span class="token operator">==</span> <span class="token string">'cube'</span><span class="token punctuation">:</span>
        <span class="token keyword">def</span> <span class="token function">cube</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> x<span class="token operator">**</span><span class="token number">3</span>
        <span class="token keyword">return</span> cube  <span class="token comment" spellcheck="true"># 返回一个函数对象</span>

<span class="token comment" spellcheck="true"># 获取一个函数</span>
operation <span class="token operator">=</span> get_operation<span class="token punctuation">(</span><span class="token string">'square'</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>operation<span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 输出25，因为5的平方是25</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>get_operation<span class="token punctuation">(</span><span class="token string">"cube"</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment" spellcheck="true"># 输出125，因为返回的函数对象是绑定到cube这个函数上的，所以调用cube(5)得到的值就是125</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>但是在<code>PEP 612</code>之前，Python的类型提示系统并不能很好地处理函数作为参数和返回值的情况。通过引入<code>ParamSpec</code>和<code>Concatenate</code>，我们可以更准确地描述高阶函数的类型。</p>
<h4 id="PEP-604-Allow-writing-union-types-as-X-Y"><a href="#PEP-604-Allow-writing-union-types-as-X-Y" class="headerlink" title="PEP 604: Allow writing union types as X | Y"></a>PEP 604: Allow writing union types as X | Y</h4><p><code>PEP 604</code>引入了一种新的方式来表示一个变量可能是多种类型，即使用<code>X | Y</code>的语法。这是Python类型注解中的一个改进，使得表示类型变得更加简洁和直观。</p>
<p>在<code>PEP 604</code>之前，如果你想表示一个变量可以是多种类型中的一种，你需要使用<code>typing.Union</code>。例如，如果一个变量可以是整数或字符串，你需要这样表示：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> Union

<span class="token keyword">def</span> <span class="token function">func</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">pass</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p><code>PEP 604</code>引入了<code>|</code>运算符，使得你可以更简洁地表示同样的意思：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">func</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> int <span class="token operator">|</span> str<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">pass</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<blockquote>
<p>介绍完了Type Hint的基础发展历史，下面我们正是介绍一下Python Type Hint的语法</p>
</blockquote>
<h2 id="3-Type-Hint基础"><a href="#3-Type-Hint基础" class="headerlink" title="3. Type Hint基础"></a>3. Type Hint基础</h2><h3 id="3-1-变量注释——基础"><a href="#3-1-变量注释——基础" class="headerlink" title="3.1 变量注释——基础"></a>3.1 变量注释——基础</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># 为变量注解类型的基本方式</span>
age<span class="token punctuation">:</span> int <span class="token operator">=</span> <span class="token number">1</span>

<span class="token comment" spellcheck="true"># 不需要初始化一个变量来注解它</span>
a<span class="token punctuation">:</span> int

<span class="token comment" spellcheck="true"># 在条件分支中这样做可以帮助代码检查工具和补全工具</span>
child<span class="token punctuation">:</span> bool
<span class="token keyword">if</span> age <span class="token operator">&lt;</span> <span class="token number">18</span><span class="token punctuation">:</span>
    child <span class="token operator">=</span> <span class="token boolean">True</span>
<span class="token keyword">else</span><span class="token punctuation">:</span>
    child <span class="token operator">=</span> <span class="token boolean">False</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="3-2-变量注释——进阶"><a href="#3-2-变量注释——进阶" class="headerlink" title="3.2 变量注释——进阶"></a>3.2 变量注释——进阶</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># 对于大多数类型，只需要在注解中使用类型的名称</span>
<span class="token comment" spellcheck="true"># 注意，IDE通常可以从变量的值推断出变量的类型，所以从技术上讲，这些注解是多余的</span>
x<span class="token punctuation">:</span> int <span class="token operator">=</span> <span class="token number">1</span>
x<span class="token punctuation">:</span> float <span class="token operator">=</span> <span class="token number">1.0</span>
x<span class="token punctuation">:</span> bool <span class="token operator">=</span> <span class="token boolean">True</span>
x<span class="token punctuation">:</span> str <span class="token operator">=</span> <span class="token string">"test"</span>
x<span class="token punctuation">:</span> bytes <span class="token operator">=</span> b<span class="token string">"test"</span>

<span class="token comment" spellcheck="true"># 对于Python 3.9+，列表和集合项的类型放在方括号中</span>
x<span class="token punctuation">:</span> list<span class="token punctuation">[</span>int<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>
x<span class="token punctuation">:</span> set<span class="token punctuation">[</span>int<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">6</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">}</span>

<span class="token comment" spellcheck="true"># 对于字典，需要指定键和值的类型</span>
x<span class="token punctuation">:</span> dict<span class="token punctuation">[</span>str<span class="token punctuation">,</span> float<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">"field"</span><span class="token punctuation">:</span> <span class="token number">2.0</span><span class="token punctuation">}</span>  <span class="token comment" spellcheck="true"># Python 3.9+</span>

<span class="token comment" spellcheck="true"># 对于固定大小的元组，需要指定所有元素的类型</span>
x<span class="token punctuation">:</span> tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">,</span> float<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">"yes"</span><span class="token punctuation">,</span> <span class="token number">7.5</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># Python 3.9+</span>

<span class="token comment" spellcheck="true"># 对于可变大小的元组，指定元素的默认类型然后使用省略号</span>
x<span class="token punctuation">:</span> tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># Python 3.9+</span>

<span class="token comment" spellcheck="true"># 在Python 3.8及更早版本中，容器类型的名称是大写的，并且是从'typing'模块导入的</span>
<span class="token keyword">from</span> typing <span class="token keyword">import</span> List<span class="token punctuation">,</span> Set<span class="token punctuation">,</span> Dict<span class="token punctuation">,</span> Tuple
x<span class="token punctuation">:</span> List<span class="token punctuation">[</span>int<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>
x<span class="token punctuation">:</span> Set<span class="token punctuation">[</span>int<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">6</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">}</span>
x<span class="token punctuation">:</span> Dict<span class="token punctuation">[</span>str<span class="token punctuation">,</span> float<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">"field"</span><span class="token punctuation">:</span> <span class="token number">2.0</span><span class="token punctuation">}</span>
x<span class="token punctuation">:</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">,</span> float<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">"yes"</span><span class="token punctuation">,</span> <span class="token number">7.5</span><span class="token punctuation">)</span>
x<span class="token punctuation">:</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span>

<span class="token keyword">from</span> typing <span class="token keyword">import</span> Union<span class="token punctuation">,</span> Optional

<span class="token comment" spellcheck="true"># 在Python 3.10+上，当变量可能是几种类型之一时，使用 | 运算符</span>
x<span class="token punctuation">:</span> list<span class="token punctuation">[</span>int <span class="token operator">|</span> str<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token string">"test"</span><span class="token punctuation">,</span> <span class="token string">"fun"</span><span class="token punctuation">]</span>  <span class="token comment" spellcheck="true"># Python 3.10+</span>
<span class="token comment" spellcheck="true"># 在早期版本中，使用Union</span>
x<span class="token punctuation">:</span> list<span class="token punctuation">[</span>Union<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token string">"test"</span><span class="token punctuation">,</span> <span class="token string">"fun"</span><span class="token punctuation">]</span>

<span class="token comment" spellcheck="true"># 对于可能为None的变量，使用Optional[X]，Optional[X]与X | None 或 Union[X, None]相同</span>
x<span class="token punctuation">:</span> Optional<span class="token punctuation">[</span>str<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"something"</span> <span class="token keyword">if</span> some_condition<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">else</span> None
<span class="token keyword">if</span> x <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># 静态类型检查工具和补全工具会自动推理出来在这里x不会是None，因为有if语句</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span>x<span class="token punctuation">.</span>upper<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment" spellcheck="true"># 如果某个变量可能是None，但是通过前面代码的逻辑你知道在这里这个永远不会是None，则使用断言通知代码补全工具和静态检查工具</span>
<span class="token keyword">assert</span> x <span class="token keyword">is</span> <span class="token operator">not</span> None
<span class="token keyword">print</span><span class="token punctuation">(</span>x<span class="token punctuation">.</span>upper<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="3-3-函数注释"><a href="#3-3-函数注释" class="headerlink" title="3.3 函数注释"></a>3.3 函数注释</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> Any<span class="token punctuation">,</span> Callable<span class="token punctuation">,</span> Iterator<span class="token punctuation">,</span> Union<span class="token punctuation">,</span> Optional

<span class="token comment" spellcheck="true"># 为一个函数进行注释</span>
<span class="token keyword">def</span> <span class="token function">stringify</span><span class="token punctuation">(</span>num<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> str<span class="token punctuation">:</span>
    <span class="token keyword">return</span> str<span class="token punctuation">(</span>num<span class="token punctuation">)</span>

<span class="token comment" spellcheck="true"># 多个参数的函数则依次注释即可</span>
<span class="token keyword">def</span> <span class="token function">plus</span><span class="token punctuation">(</span>num1<span class="token punctuation">:</span> int<span class="token punctuation">,</span> num2<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> int<span class="token punctuation">:</span>
    <span class="token keyword">return</span> num1 <span class="token operator">+</span> num2

<span class="token comment" spellcheck="true"># 如果一个函数不返回值，使用None作为返回类型</span>
<span class="token comment" spellcheck="true"># 参数的默认值在类型注解后面</span>
<span class="token keyword">def</span> <span class="token function">show</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> str<span class="token punctuation">,</span> excitement<span class="token punctuation">:</span> int <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span>value <span class="token operator">+</span> <span class="token string">"!"</span> <span class="token operator">*</span> excitement<span class="token punctuation">)</span>

<span class="token comment" spellcheck="true"># 注意，没有类型的参数是动态类型的（被视为Any）</span>
<span class="token comment" spellcheck="true"># 而没有任何注解的函数不会被检查</span>
<span class="token keyword">def</span> <span class="token function">untyped</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">:</span>                                        <span class="token comment" spellcheck="true"># 等价于 def untyped(x: Any)</span>
    x<span class="token punctuation">.</span>anything<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span> <span class="token operator">+</span> <span class="token string">"string"</span>      <span class="token comment" spellcheck="true"># 没有错误</span>


<span class="token comment" spellcheck="true"># 使用Callable注解一个可调用的变量，即函数对象</span>
<span class="token comment" spellcheck="true"># 第一个[]中是函数接受的参数，第二个类型式返回值类型</span>
x<span class="token punctuation">:</span> Callable<span class="token punctuation">[</span><span class="token punctuation">[</span>int<span class="token punctuation">,</span> float<span class="token punctuation">]</span><span class="token punctuation">,</span> float<span class="token punctuation">]</span> <span class="token operator">=</span> f
<span class="token keyword">def</span> <span class="token function">register</span><span class="token punctuation">(</span>callback<span class="token punctuation">:</span> Callable<span class="token punctuation">[</span><span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token comment" spellcheck="true"># 一个生成器函数产生ints，其实就是一个返回ints迭代器的函数，因此使用Iterator进行注解</span>
<span class="token keyword">def</span> <span class="token function">gen</span><span class="token punctuation">(</span>n<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Iterator<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">:</span>
    i <span class="token operator">=</span> <span class="token number">0</span>
    <span class="token keyword">while</span> i <span class="token operator">&lt;</span> n<span class="token punctuation">:</span>
        <span class="token keyword">yield</span> i
        i <span class="token operator">+=</span> <span class="token number">1</span>

<span class="token comment" spellcheck="true"># 当然，你可以把一个函数注解分成多行</span>
<span class="token keyword">def</span> <span class="token function">send_email</span><span class="token punctuation">(</span>address<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
               sender<span class="token punctuation">:</span> str<span class="token punctuation">,</span>
               cc<span class="token punctuation">:</span> Optional<span class="token punctuation">[</span>list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
               bcc<span class="token punctuation">:</span> Optional<span class="token punctuation">[</span>list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
               subject<span class="token punctuation">:</span> str <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">,</span>
               body<span class="token punctuation">:</span> Optional<span class="token punctuation">[</span>list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> None
               <span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> bool<span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token comment" spellcheck="true"># 可以为位置参数和关键字参数添加注解</span>
<span class="token keyword">def</span> <span class="token function">quux</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> int<span class="token punctuation">,</span> <span class="token operator">/</span><span class="token punctuation">,</span> <span class="token operator">*</span><span class="token punctuation">,</span> y<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
    <span class="token keyword">pass</span>

quux<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> y<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 正确</span>
quux<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 错误：对于"quux"来说，只有一个位置限定参数，即x</span>
quux<span class="token punctuation">(</span>x<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">,</span> y<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 错误：对于"quux"来说，"x"是意外的关键字参数</span>

<span class="token comment" spellcheck="true"># 下面表示每个位置参数和每个关键字参数都是"str"</span>
<span class="token keyword">def</span> <span class="token function">call</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token operator">*</span>args<span class="token punctuation">:</span> str<span class="token punctuation">,</span> <span class="token operator">**</span>kwargs<span class="token punctuation">:</span> str<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> str<span class="token punctuation">:</span>
    reveal_type<span class="token punctuation">(</span>args<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 揭示的类型是 "tuple[str, ...]"</span>
    reveal_type<span class="token punctuation">(</span>kwargs<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 揭示的类型是 "dict[str, str]"</span>
    request <span class="token operator">=</span> make_request<span class="token punctuation">(</span><span class="token operator">*</span>args<span class="token punctuation">,</span> <span class="token operator">**</span>kwargs<span class="token punctuation">)</span>
    <span class="token keyword">return</span> self<span class="token punctuation">.</span>do_api_query<span class="token punctuation">(</span>request<span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="3-4-类注解"><a href="#3-4-类注解" class="headerlink" title="3.4 类注解"></a>3.4 类注解</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># 银行账户类</span>
<span class="token keyword">class</span> <span class="token class-name">BankAccount</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># "__init__" 方法不返回任何内容，所以它和其他不返回任何内容的方法一样，为其注解None的返回类型</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> account_name<span class="token punctuation">:</span> str<span class="token punctuation">,</span> initial_balance<span class="token punctuation">:</span> int <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># 补全工具和静态检查工具可以根据参数的类型推断出这些实例变量的正确类型</span>
        self<span class="token punctuation">.</span>account_name <span class="token operator">=</span> account_name
        self<span class="token punctuation">.</span>balance <span class="token operator">=</span> initial_balance

    <span class="token comment" spellcheck="true"># 对于实例方法，省略 "self" 的类型</span>
    <span class="token keyword">def</span> <span class="token function">deposit</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> amount<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>balance <span class="token operator">+=</span> amount

    <span class="token keyword">def</span> <span class="token function">withdraw</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> amount<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>balance <span class="token operator">-=</span> amount

<span class="token comment" spellcheck="true"># 用户定义的类可以用于注解</span>
account<span class="token punctuation">:</span> BankAccount <span class="token operator">=</span> BankAccount<span class="token punctuation">(</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">transfer</span><span class="token punctuation">(</span>src<span class="token punctuation">:</span> BankAccount<span class="token punctuation">,</span> dst<span class="token punctuation">:</span> BankAccount<span class="token punctuation">,</span> amount<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
    src<span class="token punctuation">.</span>withdraw<span class="token punctuation">(</span>amount<span class="token punctuation">)</span>
    dst<span class="token punctuation">.</span>deposit<span class="token punctuation">(</span>amount<span class="token punctuation">)</span>

<span class="token comment" spellcheck="true"># 审计账户是银行账户的派生类</span>
<span class="token keyword">class</span> <span class="token class-name">AuditedBankAccount</span><span class="token punctuation">(</span>BankAccount<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># 可以在类中为类属性进行注解</span>
    audit_log<span class="token punctuation">:</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> account_name<span class="token punctuation">:</span> str<span class="token punctuation">,</span> initial_balance<span class="token punctuation">:</span> int <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        super<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__init__<span class="token punctuation">(</span>account_name<span class="token punctuation">,</span> initial_balance<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>audit_log<span class="token punctuation">:</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>

    <span class="token keyword">def</span> <span class="token function">deposit</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> amount<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>audit_log<span class="token punctuation">.</span>append<span class="token punctuation">(</span>f<span class="token string">"Deposited {amount}"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>balance <span class="token operator">+=</span> amount

    <span class="token keyword">def</span> <span class="token function">withdraw</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> amount<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>audit_log<span class="token punctuation">.</span>append<span class="token punctuation">(</span>f<span class="token string">"Withdrew {amount}"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>balance <span class="token operator">-=</span> amount

audited <span class="token operator">=</span> AuditedBankAccount<span class="token punctuation">(</span><span class="token string">"Bob"</span><span class="token punctuation">,</span> <span class="token number">300</span><span class="token punctuation">)</span>
<span class="token comment" spellcheck="true"># 补全工具和代码检查工具可以接受鸭子类型，即接受 BankAccount 的函数也接受 BankAccount 的任何子类</span>
transfer<span class="token punctuation">(</span>audited<span class="token punctuation">,</span> account<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 类型检查通过！</span>

<span class="token comment" spellcheck="true"># 可以使用 ClassVar 来注解一个类属性</span>
<span class="token keyword">class</span> <span class="token class-name">Car</span><span class="token punctuation">:</span>
    seats<span class="token punctuation">:</span> ClassVar<span class="token punctuation">[</span>int<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">4</span>
    passengers<span class="token punctuation">:</span> ClassVar<span class="token punctuation">[</span>list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span>

<span class="token comment" spellcheck="true"># 如果你想在你的类上有动态属性，则需要重写 "__setattr__" 或 "__getattr__"</span>
<span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># 这将允许给 A.x 赋值，代码检查中会要求 A.x 的类型和value的类型一致</span>
    <span class="token comment" spellcheck="true"># (使用 "value: Any" 允许任意类型)</span>
    <span class="token keyword">def</span> <span class="token function">__setattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">:</span> str<span class="token punctuation">,</span> value<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    <span class="token comment" spellcheck="true"># 这将允许访问 A.x，代码检查中会要求 A.x 的类型和返回值的类型一致</span>
    <span class="token keyword">def</span> <span class="token function">__getattr__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> name<span class="token punctuation">:</span> str<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> int<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

a<span class="token punctuation">.</span>foo <span class="token operator">=</span> <span class="token number">42</span>  <span class="token comment" spellcheck="true"># 正常工作</span>
a<span class="token punctuation">.</span>bar <span class="token operator">=</span> <span class="token string">'Ex-parrot'</span>  <span class="token comment" spellcheck="true"># 类型检查失败</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="3-5-和静态检查工具以及补全工具交互"><a href="#3-5-和静态检查工具以及补全工具交互" class="headerlink" title="3.5 和静态检查工具以及补全工具交互"></a>3.5 和静态检查工具以及补全工具交互</h3><p>可以使用一些特定的约定和静态检查工具和补全工具进行交互</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> Union<span class="token punctuation">,</span> Any<span class="token punctuation">,</span> Optional<span class="token punctuation">,</span> TYPE_CHECKING<span class="token punctuation">,</span> cast

<span class="token comment" spellcheck="true"># 写代码的时候如果想知道某个表达式，可以让代码补全工具或者静态检查工具给出他推断出的类型，即把表达式包裹在reveal_type()中。</span>
<span class="token comment" spellcheck="true"># 静态检查工具和补全工具将打印一个带有类型的错误消息，在运行代码之前再删除它。</span>
reveal_type<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 类型是 "builtins.int"</span>

<span class="token comment" spellcheck="true"># 如果你用一个空容器或 "None" 初始化一个变量，就要显式的进行类型注解来帮助静态检查工具和补全工具</span>
x<span class="token punctuation">:</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
x<span class="token punctuation">:</span> Optional<span class="token punctuation">[</span>str<span class="token punctuation">]</span> <span class="token operator">=</span> None

<span class="token keyword">def</span> <span class="token function">mystery_function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># Magic happens here</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token comment" spellcheck="true"># 如果你不知道某个变量的类型，或者它太动态以至于很难写出一个类型，就使用 Any</span>
x<span class="token punctuation">:</span> Any <span class="token operator">=</span> mystery_function<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment" spellcheck="true"># 这样对变量进行任何操作静态检查工具和补全工具都不会报错！</span>
x<span class="token punctuation">.</span>whatever<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> x<span class="token punctuation">[</span><span class="token string">"you"</span><span class="token punctuation">]</span> <span class="token operator">+</span> x<span class="token punctuation">(</span><span class="token string">"want"</span><span class="token punctuation">)</span> <span class="token operator">-</span> any<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token operator">and</span> all<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">is</span> super  <span class="token comment" spellcheck="true"># 没有错误</span>

<span class="token comment" spellcheck="true"># 当补全工具或者静态检查工具怀疑你有错误但是你确定这里是没问题的时候，可以使用 "type: ignore" 注释来忽略某个错误，</span>
<span class="token comment" spellcheck="true"># PS：最好添加一个解释的注释。</span>
x <span class="token operator">=</span> confusing_function<span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># type: ignore  # confusing_function在这里不会返回None，因为...</span>

<span class="token comment" spellcheck="true"># "cast" 是一个辅助函数，它可以强制让静态检查工具和补全工具使用你提供的类型。它只用于类型检查，在运行时会被忽略</span>
a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span>                                    <span class="token comment" spellcheck="true"># a的类型是 list[int]</span>
b <span class="token operator">=</span> cast<span class="token punctuation">(</span>list<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 让 b = a 的同时注解 b 的类型是 list[int]</span>
c <span class="token operator">=</span> cast<span class="token punctuation">(</span>list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 让 c = a 的同时注解 c 的类型是 list[str]，但真正在运行的时候还是list[int]</span>
reveal_type<span class="token punctuation">(</span>c<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 类型是 "builtins.list[builtins.str]"</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span>  <span class="token comment" spellcheck="true"># 仍然打印 [4] ... 在运行时对象没有改变或被转型</span>

<span class="token comment" spellcheck="true"># 使用 "TYPE_CHECKING"来指定静态检查工具检查时运行的代码，这些代码不会在运行时执行</span>
<span class="token keyword">if</span> TYPE_CHECKING<span class="token punctuation">:</span>
    <span class="token keyword">import</span> json
<span class="token keyword">else</span><span class="token punctuation">:</span>
    <span class="token keyword">import</span> orjson <span class="token keyword">as</span> json  <span class="token comment" spellcheck="true"># 代码检查时不会运行这句话</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h2 id="4-Type-Hint中级"><a href="#4-Type-Hint中级" class="headerlink" title="4. Type Hint中级"></a>4. Type Hint中级</h2><h3 id="4-1-容器类型注解"><a href="#4-1-容器类型注解" class="headerlink" title="4.1 容器类型注解"></a>4.1 容器类型注解</h3><p><code>typing</code>模块中定义的<code>Mapping</code>, <code>MutableMapping</code>, <code>Sequence</code>, <code>Iterable</code>等都是抽象基类（ABCs, Abstract Base Classes），它们为Python的内置容器类型提供了一种形式化的方式来描述其行为。这些抽象基类定义了一组方法，任何实现了这些方法的类都可以被认为是这个抽象基类的子类。</p>
<ol>
<li><strong>Iterable</strong>：<code>Iterable</code>是最基本的抽象基类，代表了所有可以迭代的对象。任何定义了<code>__iter__</code>方法的对象都可以被认为是<code>Iterable</code>。这包括所有的序列类型（如<code>list</code>，<code>str</code>，<code>tuple</code>），也包括非序列类型（如<code>dict</code>，<code>set</code>）。</li>
<li><strong>Sequence</strong>：<code>Sequence</code>是<code>Iterable</code>的子类，代表了所有的序列类型。除了要求实现<code>__iter__</code>方法，<code>Sequence</code>还要求实现<code>__len__</code>和<code>__getitem__</code>方法。</li>
<li><strong>Mapping</strong>：<code>Mapping</code>也是<code>Iterable</code>的子类，代表了所有的映射类型（如<code>dict</code>）。<code>Mapping</code>要求实现<code>__iter__</code>，<code>__len__</code>和<code>__getitem__</code>方法，同时还要求实现<code>keys</code>，<code>items</code>，<code>values</code>，<code>get</code>，<code>__contains__</code>，和<code>__eq__</code>方法。</li>
<li><strong>MutableMapping</strong>：<code>MutableMapping</code>是<code>Mapping</code>的子类，代表了所有可变的映射类型。除了<code>Mapping</code>的所有方法，<code>MutableMapping</code>还要求实现<code>__setitem__</code>，<code>__delitem__</code>，<code>pop</code>，<code>popitem</code>，<code>clear</code>，<code>update</code>，<code>setdefault</code>等方法。</li>
</ol>
<p>这些抽象基类在类型注解中非常有用，因为它们让我们可以描述一个对象应该具有的行为，而不是对象的具体类型。例如，如果一个函数接受一个<code>Iterable</code>参数，那么我们可以传入任何可迭代的对象，不论是<code>list</code>，<code>str</code>，<code>tuple</code>，还是<code>dict</code>，<code>set</code>等。</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> Mapping<span class="token punctuation">,</span> MutableMapping<span class="token punctuation">,</span> Sequence<span class="token punctuation">,</span> Iterable

<span class="token comment" spellcheck="true"># 接受支持__iter__方法的对象作为参数的函数，可以使用 Iterable 进行注释</span>
<span class="token comment" spellcheck="true"># 接受支持__iter__、__len__ 和 __getitem__方法的对象作为参数的函数，使用 Sequence 进行注释</span>
<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>ints<span class="token punctuation">:</span> Iterable<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token punctuation">[</span>str<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">for</span> x <span class="token keyword">in</span> ints<span class="token punctuation">]</span>

f<span class="token punctuation">(</span>range<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token comment" spellcheck="true"># Mapping 描述了一个我们不会改变的类似字典的对象，即在 Iterable 的基础上由 __getitem__ 方法</span>
<span class="token comment" spellcheck="true"># MutableMapping 描述了我们可能会改变的对象，即在 Mapping 的基础上实现了 __setitem__ 方法</span>
<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>my_mapping<span class="token punctuation">:</span> Mapping<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> list<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">:</span>
    my_mapping<span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">'maybe'</span>  <span class="token comment" spellcheck="true"># 尝试设置 Mapping 类型，静态检查工具会报错</span>
    <span class="token keyword">return</span> list<span class="token punctuation">(</span>my_mapping<span class="token punctuation">.</span>keys<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

f<span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">3</span><span class="token punctuation">:</span> <span class="token string">'yes'</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">:</span> <span class="token string">'no'</span><span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>my_mapping<span class="token punctuation">:</span> MutableMapping<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> set<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">:</span>
    my_mapping<span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">'maybe'</span>  <span class="token comment" spellcheck="true"># 设置 MutableMapping 是没问题的</span>
    <span class="token keyword">return</span> set<span class="token punctuation">(</span>my_mapping<span class="token punctuation">.</span>values<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

f<span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">3</span><span class="token punctuation">:</span> <span class="token string">'yes'</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">:</span> <span class="token string">'no'</span><span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">import</span> sys
<span class="token keyword">from</span> typing <span class="token keyword">import</span> IO

<span class="token comment" spellcheck="true"># 对于应该接受或返回 open() 调用返回的对象的函数，使用 IO[str] 或 IO[bytes]</span>
<span class="token comment" spellcheck="true"># （注意 IO 没有区分读取，写入或其他模式）</span>
<span class="token keyword">def</span> <span class="token function">get_sys_IO</span><span class="token punctuation">(</span>mode<span class="token punctuation">:</span> str <span class="token operator">=</span> <span class="token string">'w'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> IO<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> mode <span class="token operator">==</span> <span class="token string">'w'</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> sys<span class="token punctuation">.</span>stdout
    <span class="token keyword">elif</span> mode <span class="token operator">==</span> <span class="token string">'r'</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> sys<span class="token punctuation">.</span>stdin
    <span class="token keyword">else</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> sys<span class="token punctuation">.</span>stdout
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="4-2-前向注解"><a href="#4-2-前向注解" class="headerlink" title="4.2 前向注解"></a>4.2 前向注解</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># 你可能需要在类定义之前就引用它。</span>
<span class="token comment" spellcheck="true"># 这被称为"前向引用"。</span>
<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>foo<span class="token punctuation">:</span> A<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> int<span class="token punctuation">:</span>  <span class="token comment" spellcheck="true"># 在运行时会失败，因为'A'没有被定义</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token comment" spellcheck="true"># 然而，如果你添加了以下特殊的导入：</span>
<span class="token keyword">from</span> __future__ <span class="token keyword">import</span> annotations
<span class="token comment" spellcheck="true"># 在运行时它会工作，只要文件后面有一个同名的类，类型检查也会成功</span>
<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>foo<span class="token punctuation">:</span> A<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> int<span class="token punctuation">:</span>  <span class="token comment" spellcheck="true"># 正常</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token comment" spellcheck="true"># 另一个选择就是把类型放在引号里</span>
<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>foo<span class="token punctuation">:</span> <span class="token string">'A'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> int<span class="token punctuation">:</span>  <span class="token comment" spellcheck="true"># 也是正常的</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># 如果你需要在类的定义内部的类型注解中引用该类，也可能会遇到这种情况</span>
    @classmethod
    <span class="token keyword">def</span> <span class="token function">create</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> A<span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="4-3-类型别名-TypeAlias"><a href="#4-3-类型别名-TypeAlias" class="headerlink" title="4.3 类型别名 TypeAlias"></a>4.3 类型别名 TypeAlias</h3><p>在某些情况下，类型注解可能会变得很长并且难以输入：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Union<span class="token punctuation">[</span>list<span class="token punctuation">[</span>dict<span class="token punctuation">[</span>tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> set<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> tuple<span class="token punctuation">[</span>str<span class="token punctuation">,</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>当遇到这样的情况时，在早期版本可以将类型注解赋值给一个变量来定义一个类型别名：</p>
<pre class="line-numbers language-python"><code class="language-python">AliasType <span class="token operator">=</span> Union<span class="token punctuation">[</span>list<span class="token punctuation">[</span>dict<span class="token punctuation">[</span>tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> set<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> tuple<span class="token punctuation">[</span>str<span class="token punctuation">,</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span>

<span class="token comment" spellcheck="true"># 现在我们可以在代替完整名称的地方使用AliasType：</span>

<span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> AliasType<span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<blockquote>
<p>注意：类型别名并不会创建一个新的类型。它只是另一种类型的简写表示 - 它等同于目标类型，除了泛型别名。</p>
</blockquote>
<p>在PEP 613中引入的了类型别名，显示的支持了对类型别名变量的注解。这样显式注解类型别名是可以提高可读性：</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> TypeAlias  <span class="token comment" spellcheck="true"># 在Python 3.9及更早版本中使用 "from typing_extensions"</span>

AliasType<span class="token punctuation">:</span> TypeAlias <span class="token operator">=</span> Union<span class="token punctuation">[</span>list<span class="token punctuation">[</span>dict<span class="token punctuation">[</span>tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> set<span class="token punctuation">[</span>int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> tuple<span class="token punctuation">[</span>str<span class="token punctuation">,</span> list<span class="token punctuation">[</span>str<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">Jack Wang</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://jackwang0107.github.io/2023/11/24/python-type-hint/">https://jackwang0107.github.io/2023/11/24/python-type-hint/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/about" target="_blank">Jack Wang</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/Python/">
                                    <span class="chip bg-color">Python</span>
                                </a>
                            
                                <a href="/tags/%E6%9D%82%E9%A1%B9/">
                                    <span class="chip bg-color">杂项</span>
                                </a>
                            
                                <a href="/tags/Python-Type-Hint/">
                                    <span class="chip bg-color">Python Type Hint</span>
                                </a>
                            
                                <a href="/tags/Python-%E7%B1%BB%E5%9E%8B%E6%B3%A8%E9%87%8A/">
                                    <span class="chip bg-color">Python 类型注释</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.png" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.jpg" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/2023/11/25/python-stub-file/">
                    <div class="card-image">
                        
                        <img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/20201221215820109.png" class="responsive-img" alt="Python Stub File">
                        
                        <span class="card-title">Python Stub File</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            本文介绍了Python Stub File，即.pyi文件
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2023-11-25
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-user fa-fw"></i>
                            Jack Wang
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Python/">
                        <span class="chip bg-color">Python</span>
                    </a>
                    
                    <a href="/tags/%E6%9D%82%E9%A1%B9/">
                        <span class="chip bg-color">杂项</span>
                    </a>
                    
                    <a href="/tags/Python-Stub-File/">
                        <span class="chip bg-color">Python Stub File</span>
                    </a>
                    
                    <a href="/tags/pyi/">
                        <span class="chip bg-color">.pyi</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2023/11/24/shell-jiao-ben-jin-jie-tiao-jian-yu-ju/">
                    <div class="card-image">
                        
                        <img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/shell-scripting-if-statement-0-1667724889.jpg" class="responsive-img" alt="Shell脚本进阶：条件语句">
                        
                        <span class="card-title">Shell脚本进阶：条件语句</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            本文介绍了Shell的条件语句
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2023-11-24
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/Linux%E5%AE%9E%E7%94%A8%E6%8A%80%E5%B7%A7/" class="post-category">
                                    Linux实用技巧
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Linux/">
                        <span class="chip bg-color">Linux</span>
                    </a>
                    
                    <a href="/tags/Ubuntu/">
                        <span class="chip bg-color">Ubuntu</span>
                    </a>
                    
                    <a href="/tags/Shell/">
                        <span class="chip bg-color">Shell</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('120')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE' || selection.getRangeAt(0).commonAncestorContainer.nodeName === 'CODE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + '来源: JackWang&#39;s Blog<br />'
            + '文章作者: Jack Wang<br />'
            + '文章链接: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('2'),
            headingSelector: 'h1, h2, h3, h4, h5, h6'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h1, h2, h3, h4, h5, h6').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    

    <div class="container row center-align"
         style="margin-bottom: 15px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2021-2023</span>
            
            <a href="/about" target="_blank">Jack Wang</a>
            <!-- |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a> -->
            <!-- |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a> -->
            <br>
            
                &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                        class="white-color">603.8k</span>
            
            
            
                
            
            
                <span id="busuanzi_container_site_pv">
                &nbsp;|&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;
                    <span id="busuanzi_value_site_pv" class="white-color"></span>
            </span>
            
            
                <span id="busuanzi_container_site_uv">
                &nbsp;|&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;
                    <span id="busuanzi_value_site_uv" class="white-color"></span>
            </span>
            
            <br>

            <!-- 运行天数提醒. -->
            
                <span id="sitetime"> Loading ...</span>
                <script>
                    var calcSiteTime = function () {
                        var seconds = 1000;
                        var minutes = seconds * 60;
                        var hours = minutes * 60;
                        var days = hours * 24;
                        var years = days * 365;
                        var today = new Date();
                        var startYear = "2021";
                        var startMonth = "11";
                        var startDate = "12";
                        var startHour = "0";
                        var startMinute = "0";
                        var startSecond = "0";
                        var todayYear = today.getFullYear();
                        var todayMonth = today.getMonth() + 1;
                        var todayDate = today.getDate();
                        var todayHour = today.getHours();
                        var todayMinute = today.getMinutes();
                        var todaySecond = today.getSeconds();
                        var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                        var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                        var diff = t2 - t1;
                        var diffYears = Math.floor(diff / years);
                        var diffDays = Math.floor((diff / days) - diffYears * 365);

                        // 区分是否有年份.
                        var language = 'zh-CN';
                        if (startYear === String(todayYear)) {
                            document.getElementById("year").innerHTML = todayYear;
                            var daysTip = 'This site has been running for ' + diffDays + ' days';
                            if (language === 'zh-CN') {
                                daysTip = '本站已运行 ' + diffDays + ' 天';
                            } else if (language === 'zh-HK') {
                                daysTip = '本站已運行 ' + diffDays + ' 天';
                            }
                            document.getElementById("sitetime").innerHTML = daysTip;
                        } else {
                            document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                            var yearsAndDaysTip = 'This site has been running for ' + diffYears + ' years and '
                                + diffDays + ' days';
                            if (language === 'zh-CN') {
                                yearsAndDaysTip = '本站已运行 ' + diffYears + ' 年 ' + diffDays + ' 天';
                            } else if (language === 'zh-HK') {
                                yearsAndDaysTip = '本站已運行 ' + diffYears + ' 年 ' + diffDays + ' 天';
                            }
                            document.getElementById("sitetime").innerHTML = yearsAndDaysTip;
                        }
                    }

                    calcSiteTime();
                </script>
            
            <br>
            
                <span id="icp"><img src="/medias/icp.png"
                                    style="vertical-align: text-bottom;"/>
                <a href="https://beian.miit.gov.cn" target="_blank">陕ICP备2021014294号-1</a>
            </span>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/jackwang0108" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:2232123545@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=2232123545" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 2232123545" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    

    
        
        <script type="text/javascript">
            // 只在桌面版网页启用特效
            var windowWidth = $(window).width();
            if (windowWidth > 768) {
                document.write('<script type="text/javascript" src="/libs/others/sakura.js"><\/script>');
            }
        </script>
    

    <!-- 雪花特效 -->
    

    <!-- 鼠标星星特效 -->
    

     
        <script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
        <script src="/libs/others/TencentCaptcha.js"></script>
        <button id="TencentCaptcha" data-appid="xxxxxxxxxx" data-cbfn="callback" type="button" hidden></button>
    

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    <!--腾讯兔小巢-->
    
    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
